home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / tpio24.zip / IO24.PAS < prev    next >
Pascal/Delphi Source File  |  1993-01-04  |  43KB  |  1,218 lines

  1. { IO24.PAS -- Global I/O procedures to include in programs generally
  2.   by Bill Meacham
  3.   Ver 2.0 -- includes prev_page and next_page, changes where pause text
  4.              is displayed -- 2/26/86.
  5.              Cosmetic improvements -- 4/16/86.
  6.   Ver 2.l -- Add function Pad -- 10/12/86.
  7.   Ver 2.2 -- Add ability to move cursor within input line -- 5/24/87.
  8.   Ver 2.3 -- Add proc buzz, error_buzz; add buzzes to read routines.
  9.              Converted to Turbo 4.0 -- 12/2/87
  10.              Converted to a Unit -- 12/2/87
  11.              Fix bug in Read_Real -- 1/3/88 -- TP4 cannot handle a trailing
  12.                  decimal point where TP3 could
  13.              Add home_key and end_key -- 1/24/88
  14.              Fix bug in Read_Int -- 2/18/88
  15.              Add Read_Longint and Write_Longint -- 3/19/88
  16.   Ver 2.4 -- Include video routines, etc. from DOS23 -- 3/23/88
  17.              Add procedure Deedle -- 5/1/88
  18.              Add vid_base -- 6/??/88
  19.              Add prior_fld & prior_scrn -- 7/27/88
  20.              Fix minor bug in read_real;
  21.              Remove F1 key; add + 128 to F-keys, etc. -- 8/5/88
  22.              Fixed proc Pause re: Terminating -- 1/8/89
  23.              Changed proc Beep -- 3/15/89 }
  24.  
  25. { -------------------------------------------------------------------------- }
  26.  
  27. unit io24 ;
  28. {$v-}
  29. interface
  30.  
  31. uses
  32.     crt, dos ;
  33.  
  34. const
  35.                        { ASCII values of cursor control keys, like WordStar. }
  36.     null      = $00 ;
  37.     prev_char = $13 ;  { ^S }
  38.     next_char = $04 ;  { ^D }
  39.     home_key  = $01 ;  { ^A }
  40.     end_key   = $06 ;  { ^F }
  41.     prev_fld  = $05 ;  { ^E }
  42.     next_fld  = $18 ;  { ^X }
  43.     prev_page = $12 ;  { ^R }
  44.     next_page = $03 ;  { ^C }
  45.     del_char  = $07 ;  { ^G }
  46.     del_left  = $08 ;  { ^H (Backspace) }
  47.     del_fld   = $19 ;  { ^Y }
  48.     del       = $7F ;  { Delete }
  49.     help_key  = $BB ;  { F1 key }
  50.     escape    = $1B ;
  51.     carr_rtn  = $0D ;
  52.     space     = $20 ;
  53.     filler    = $5F ;  { _ }
  54.  
  55. type
  56.     str1      = string[1] ;
  57.     str14     = string[14] ;
  58.     str_type  = string[80] ;
  59.     byteset   = set of $00 .. $FF ;
  60.  
  61. const  { Turbo typed constants -- initialized variables }
  62.  
  63.     terminating : byteset = [carr_rtn,next_fld,prev_fld,escape,next_page,prev_page] ;
  64.     adjusting   : byteset = [prev_char,next_char,home_key,end_key,del_char,del_fld,del_left] ;
  65.  
  66. (*
  67.  * NOTE -- If you do not want PgDn and PgUp to have an effect in your
  68.  * program, put the following statement in the program that Uses this unit.
  69.  * Put it before calling any of the Read routines (read_str, read_int, etc.).
  70.  *
  71.  *          terminating := terminating - [next_page, prev_page] ;
  72.  *)
  73.  
  74. var
  75.     fld, scrn,                { For field & screen cursor control }
  76.     prior_fld,                { to save what fld is before it gets changed }
  77.     prior_scrn,               { to save what scrn is before it gets changed }
  78.     bgcolor,                  { background color }
  79.     txcolor     : integer ;   { text color }
  80.     is_mono     : boolean ;   { whether monochrome screen }
  81.     vid_base    : word ;      { video base -- where video ram is }
  82.  
  83. procedure clrline (col,row : byte) ;
  84.   { clears to end of line }
  85. procedure buzz (pitch,duration : integer) ;
  86.   { makes a sound }
  87. procedure beep ;
  88.   { sounds audible tone }
  89. procedure error_buzz ;
  90.   { makes a particular sound }
  91. procedure deedle(deedles : integer);
  92.   { sounds like a telephone ring; parm deedles is number of rings }
  93. procedure do_fld_ctl (key : integer) ;
  94.   { Adjusts global FLD based on value of key, ORD of last key pressed }
  95. procedure do_scrn_ctl ;
  96.   { Checks value of FLD and adjusts value of SCRN accordingly }
  97. procedure write_str (st:str_type ; col,row:byte) ;
  98.   { writes a string on screen at column and row specified }
  99. procedure write_int (int:integer ; width,col,row:byte) ;
  100.   { writes an integer }
  101. procedure write_longint (lint:longint ; width,col,row:byte) ;
  102.   { writes a long integer }
  103. procedure set_bool (var bool : boolean) ;
  104.   { sets boolean to undefined, neither true nor false }
  105. function defined (bool : boolean) : boolean ;
  106.   { whether boolean is defined }
  107. procedure write_bool (bool:boolean ; col, row:byte) ;
  108.   { writes a boolean as 'YES' or 'NO' }
  109. procedure write_real (r:real ; width,frac,col,row:byte) ;
  110.   { writes a real }
  111. procedure keyin (var ch:char) ;
  112.   { Reads a single character from keyboard without echoing it back.
  113.     Maps function key scan codes to single keyboard keys. }
  114. function build_str (ch : char ; n : integer) : str_type ;
  115.   { returns a string of length n of the character ch }
  116. function pad (st : str_type ; ch : char ; i : integer) : str_type ;
  117.   { Pad string with ch to length of i.
  118.     Do not let i exceed 80 (length of str_type! }
  119. function purgech (instr : str_type ; inchar : char) : str_type ;
  120.   { Purges all instances of the character from the string }
  121. function stripch (instr:str_type ; inchar:char) : str_type ;
  122.   { Strips leading instances of the character from the string }
  123. function chopch (instr:str_type ; inchar:char) : str_type ;
  124.   { Chops trailing instances of the character from the string }
  125. procedure read_str (var st:str_type ; maxlen, col, row:byte) ;
  126.   { Read String.  This procedure gets input from the keyboard one
  127.     character at a time and edits on the fly, rejecting invalid
  128.     characters.  COL and ROW tell where to begin the data input
  129.     field, and MAXLEN is the maximum length of the string to be
  130.     returned. }
  131. procedure read_int (var int:integer ; maxlen, col, row:byte) ;
  132.   { Read Integer.  This procedure gets input from the keyboard
  133.     one character at a time and edits on the fly, rejecting
  134.     invalid characters.  COL and ROW tell where to begin the data
  135.     input field, and MAXLEN is the maximum length of the integer
  136.     to be returned. }
  137. procedure read_longint (var lint:longint ; maxlen, col, row:byte) ;
  138.   { Read Long Integer.  Just like read_int. }
  139. function equal (r1,r2 : real) : boolean ;
  140.   { Tests functional equality of two real numbers.
  141.     True if r1 = r2. }
  142. function greater (r1,r2 : real) : boolean ;
  143.   { Tests functional inequality of two real numbers.
  144.     True if r1 > r2. }
  145. procedure read_real (var r:real ; maxlen,frac,col,row:byte) ;
  146.   { Read Real.  This procedure gets input from the keyboard
  147.     one character at a time and edits on the fly, rejecting
  148.     invalid characters.  COL and ROW tell where to begin the data
  149.     input field; MAXLEN is the maximum length of the string
  150.     representation of the real number, including sign and decimal
  151.     point; FRAC is the fractional part, the number of digits to
  152.     right of the decimal point. }
  153. procedure read_yn (var bool:boolean; col,row:byte) ;
  154.   { Inputs "Y" OR "N" to boolean at column and row specified,
  155.     prints "YES" or "NO."
  156.     Note -- use this when the screen control will not return
  157.     to the question and the boolean IS NOT defined before the
  158.     user answers the question.  Does not affect global FLD. }
  159. procedure read_bool (var bool:boolean; col,row:byte) ;
  160.   { Displays boolean at column and row specified, inputs "Y"
  161.     or "N" to set new value of boolean, prints "YES" or "NO."
  162.     Boolean is "forced;" user cannot cursor forward past undefined
  163.     boolean.  Pressing "Y" or "N" terminates entry. }
  164. procedure pause ;
  165.   { Prints message on bottom line, waits for user response. }
  166. procedure hard_pause ;
  167.   { Like Pause, but only accepts space bar or Escape and only goes forward }
  168.  
  169. procedure rvson ;
  170.   { turn reverse video on }
  171. procedure rvsoff ;
  172.   { turn reverse video off }
  173. procedure emphon ;
  174.   { turn emphasis on -- if text is dim, make it bright; if bright make it dim }
  175. procedure emphoff ;
  176.   { turn emphasis off }
  177. procedure assigncolors ;
  178.   { change colors on display -- border same as background, but only on CGA }
  179. procedure getdrive (var drive : str1) ;
  180.   { get current drive }
  181.  
  182. procedure show_msg (msg : str_type) ;
  183.   { Beeps, displays message centered on line 23, pauses }
  184.  
  185. { ========================================================================== }
  186.  
  187. implementation
  188.  
  189. var
  190.     regs : registers ;
  191.  
  192. { procedure gotoxy (col,row) ; -- Built-in proc in Turbo to place
  193.   cursor on screen.  Upper left is (1,1) not (0,0)! }
  194.  
  195. { procedure clrscr ; -- Built-in proc in Turbo to clear screen. }
  196.  
  197. { procedure clreol ; -- built-in proc in Turbo to clear to end of line }
  198.  
  199. { -------------------------------------------------------------------------- }
  200.  
  201. procedure clrline (col,row : byte) ;
  202.     begin
  203.         gotoxy (col,row) ;
  204.         clreol
  205.     end ;
  206.  
  207. { -------------------------------------------------------------------------- }
  208.  
  209. procedure buzz (pitch,duration : integer) ;
  210.   begin
  211.     sound(pitch) ;
  212.     delay(duration) ;
  213.     nosound
  214.   end ;
  215.  
  216. { -------------------------------------------------------------------------- }
  217.  
  218. procedure beep ;
  219.   begin
  220.     buzz(456,200) ;
  221.   end ;
  222.  
  223. { -------------------------------------------------------------------------- }
  224.  
  225. procedure error_buzz ;
  226.   begin
  227.     buzz (50,100)
  228.   end ;
  229.  
  230. { -------------------------------------------------------------------------- }
  231.  
  232. {->>>>Deedle<<<<-----------------------------------------------}
  233. {                                                              }
  234. { Filename: DEEDLE.SRC -- Last Modified 10/20/85               }
  235. {                                                              }
  236. { This routine makes a sound not unlike certain electronic     }
  237. { telephone ringers you hear in lawyers' offices.  The number  }
  238. { of "deedles" is given by the value passed in Deedles.        }
  239. {                                                              }
  240. {        From: TURBO PASCAL SOLUTIONS by Jeff Duntemann        }
  241. {    Scott, Foresman & Co., Inc. 1987   ISBN 0-673-18584-2     }
  242. {--------------------------------------------------------------}
  243.  
  244. PROCEDURE Deedle(Deedles : Integer);
  245.  
  246. VAR I : Integer;
  247.  
  248. BEGIN
  249.   FOR I := 1 TO Deedles DO
  250.     BEGIN
  251.       Sound(800); Delay(50); Sound(500); Delay(50)
  252.     END;
  253.   NoSound
  254. END;
  255.  
  256. { -------------------------------------------------------------------------- }
  257.  
  258. procedure do_fld_ctl (key : integer) ;
  259.   { Adjusts global FLD based on value of key, the ordinal value of last key pressed }
  260.   { global
  261.         fld       : integer -- for field cursor control
  262.         prior_fld : integer -- to save what fld is before it gets changed }
  263.     begin
  264.         prior_fld := fld ;
  265.         case key of
  266.           carr_rtn, next_fld : fld := succ(fld) ;
  267.           prev_fld           : fld := pred(fld) ;
  268.           next_page          : fld := 999 ;
  269.           prev_page          : fld := -999 ;
  270.           escape             : fld := maxint ;
  271.         end  { case }
  272.     end ;  { proc do_fld_ctl }
  273.  
  274. { ------------------------------------------------------------ }
  275.  
  276. procedure do_scrn_ctl ;
  277.   { Checks value of FLD and adjusts value of SCRN accordingly }
  278.   { Global
  279.         fld, scrn  : integer -- For field and screen cursor control 
  280.         prior_scrn : integer -- to save what scrn is before it gets changed }
  281.     begin
  282.         prior_scrn := scrn ;
  283.         if fld < 1 then
  284.             scrn := pred(scrn)
  285.         else if fld = maxint then
  286.             scrn := maxint
  287.         else
  288.             scrn := succ(scrn)
  289.     end ;
  290.  
  291. { ------------------------------------------------------------ }
  292.  
  293. procedure write_str (st:str_type ; col,row:byte) ;
  294.     begin
  295.         gotoxy (col,row) ;
  296.         write (st)
  297.     end ;
  298.  
  299. { -------------------------------------------------------------------------- }
  300.  
  301. procedure write_int (int:integer ; width,col,row:byte) ;
  302.     begin
  303.         gotoxy (col,row) ;
  304.         write (int:width)
  305.     end ;
  306.  
  307. { -------------------------------------------------------------------------- }
  308.  
  309. procedure write_longint (lint:longint ; width,col,row:byte) ;
  310.     begin
  311.         gotoxy (col,row) ;
  312.         write (lint:width)
  313.     end ;
  314.  
  315. { -------------------------------------------------------------------------- }
  316.  
  317. procedure set_bool (var bool : boolean) ;
  318.   { Sets boolean to be undefined, neither true nor false.
  319.     Boolean is stored as one byte:
  320.         $80 = undefined
  321.         $01 = true
  322.         $00 = false.
  323.     Note : Turbo interprets $80 as true because it is greater than zero! }
  324.  
  325.     var
  326.         b : byte absolute bool ;
  327.     begin
  328.         b := $80
  329.     end ;  { proc set_bool }
  330.  
  331. { -------------------------------------------------------------------------- }
  332.  
  333. function defined (bool : boolean) : boolean ;
  334.   { Determines whether the boolean is defined or not }
  335.     var
  336.         b : byte absolute bool ;
  337.     begin
  338.         defined := not (b = $80)
  339.     end ;  { function defined }
  340.  
  341. { -------------------------------------------------------------------------- }
  342.  
  343. procedure write_bool (bool:boolean ; col, row:byte) ;
  344.     begin
  345.         gotoxy (col,row) ;
  346.         if not defined(bool) then
  347.             write ('___')
  348.         else if bool then
  349.             write ('YES')
  350.         else
  351.             write ('NO ')
  352.     end ;
  353.  
  354. { -------------------------------------------------------------------------- }
  355.  
  356. procedure write_real (r:real ; width,frac,col,row:byte) ;
  357.     begin
  358.         gotoxy (col,row) ;
  359.         write (r:width:frac)
  360.     end ;
  361.  
  362. { -------------------------------------------------------------------------- }
  363.  
  364. procedure keyin (var ch:char) ;
  365. { Reads a single character from keyboard without echoing it back.
  366.   Maps function key scan codes to single keyboard keys.
  367.   From Turbo 3.0 manual, page 360 -- 5/29/85
  368.   Modified for IO20 -- 2/26/86
  369.   Modified for IO23 -- 5/24/87
  370.   Modified for Turbo 4.0 -- 11/26/87
  371.   Delete F1 key; add 128 to F-keys, etc. -- 8/5/88 }
  372.  
  373.     var
  374.            c : char ;        { Character read }
  375.          key : integer ;     { ORD of character returned }
  376.  
  377.     begin
  378.         c := readkey ;                    { Get first char }
  379.         if  (ord(c) = null) then          { If there is a second ... }
  380.           begin
  381.             c := readkey ;                { Get 2nd char }
  382.             if ord(c) < 128 then
  383.                 c := chr(ord(c) + 128) ;  { Translate F keys to single value }
  384.           end ;
  385.         key := ord(c) ;
  386.  
  387.         case key of
  388.                                         { Translate F-keys and arrow keys }
  389.           191,203 : key := prev_char ;  { F5, left-arrow }
  390.           192,205 : key := next_char ;  { F6, right-arrow }
  391.           199     : key := home_key ;   { Home }
  392.           207     : key := end_key ;    { End }
  393.           189,200 : key := prev_fld ;   { F3, up-arrow }
  394.           190,208 : key := next_fld ;   { F4, down-arrow }
  395.           193,201 : key := prev_page ;  { F7, PgUp }
  396.           194,209 : key := next_page ;  { F8, PgDn }
  397.           211     : key := del_char ;   { DEL }
  398.           188     : key := del_fld ;    { F2 }
  399.                                         { CP/M-like control keys }
  400.           $0B     : key := prev_fld ;   { ^K }
  401.           $0A     : key := next_fld ;   { ^J }
  402.           $0C     : key := next_char ;  { ^L }
  403.         end ;  { case }
  404.  
  405.         ch := chr(key)                    { finally, return the character }
  406.     end ; { procedure keyin }
  407.  
  408. { ------------------------------------------------------------ }
  409.  
  410. function build_str (ch : char ; n : integer) : str_type ;
  411.   { returns a string of length n of the character ch }
  412.     var
  413.         st : str_type ;
  414.     begin
  415.         if n < 0 then
  416.             n := 0 ;
  417.         st[0] := chr(n) ;
  418.         fillchar (st[1],n,ch) ;
  419.         build_str := st
  420.     end ;  { function build_str) ;
  421.  
  422. { -------------------------------------------------------------------------- }
  423.  
  424. function pad (st : str_type ; ch : char ; i : integer) : str_type ;
  425. { Pad string with ch to length of i.  Do not let i exceed 80 (length of str_type! }
  426.   var
  427.     l : integer ;
  428.   begin
  429.     l := length(st) ;
  430.     if l < i then
  431.       begin
  432.         fillchar (st[l+1],i-l,ch) ;
  433.         st[0] := chr(i)
  434.       end ;
  435.     pad := st
  436.   end;
  437.  
  438. { ------------------------------------------------------------ }
  439.  
  440. procedure adjust_str (var st : str_type ;
  441.                       var  p : byte ;     { position of char to left of cursor }
  442.                          key,             { ord of adjusting character }
  443.             maxlen, col, row : byte ) ;
  444.   { Adjusts position of cursor within string, deletes characters, etc. }
  445.     begin
  446.       case key of
  447.         home_key  : p := 0 ;
  448.         end_key   : p := length(st) ;
  449.         prev_char : if p > 0 then
  450.                         p := pred(p)
  451.                     else
  452.                         error_buzz ;
  453.         next_char : if p < length(st) then
  454.                         p := succ(p)
  455.                     else
  456.                         error_buzz ;
  457.         del_left  : if p > 0 then
  458.                       begin
  459.                         delete (st,p,1) ;
  460.                         write (^H,copy(st,p,maxlen),chr(filler)) ;
  461.                         p := pred(p)
  462.                       end
  463.                     else
  464.                         error_buzz ;
  465.         del_char  : if p < length(st) then
  466.                       begin
  467.                         delete (st,p+1,1) ;
  468.                         write (copy(st,p+1,maxlen),chr(filler))
  469.                       end
  470.                     else
  471.                         error_buzz ;
  472.         del_fld   : begin
  473.                       st := '' ;
  474.                       p := 0  ;
  475.                       write_str (build_str(chr(filler),maxlen),col,row)
  476.                     end
  477.       end  { case }
  478.     end ; { proc adjust_str }
  479.  
  480. { -------------------------------------------------------------------------- }
  481.  
  482. function purgech (instr : str_type ; inchar : char) : str_type ;
  483.     {Purges all instances of the character from the string}
  484.     var
  485.         n      : integer ;  {Loop counter}
  486.         outstr : str_type ; {Result string}
  487.  
  488.     begin
  489.         outstr := '' ;
  490.         for n := 1 to length (instr) do
  491.                 if not (instr[n] = inchar) then
  492.                         outstr := concat (outstr, instr[n]) ;
  493.         purgech := outstr
  494.     end ;
  495.  
  496. { -------------------------------------------------------------------------- }
  497.  
  498. function stripch (instr:str_type ; inchar:char) : str_type ;
  499.     {Strips leading instances of the character from the string}
  500.     begin
  501.         while not (length(instr) = 0)
  502.         and (instr[1] = inchar) do
  503.                 delete (instr, 1, 1) ;
  504.         stripch := instr
  505.     end ;
  506.  
  507. { -------------------------------------------------------------------------- }
  508.  
  509. function chopch (instr:str_type ; inchar:char) : str_type ;
  510.     {Chops trailing instances of the character from the string}
  511.     begin
  512.         while not (length(instr) = 0)
  513.         and (instr[length(instr)] = inchar) do
  514.                 delete (instr, length(instr), 1) ;
  515.         chopch := instr
  516.     end ;
  517.  
  518. { -------------------------------------------------------------------------- }
  519.  
  520. procedure read_str (var st:str_type ; maxlen, col, row:byte) ;
  521.  
  522.   { Read String.  This procedure gets input from the keyboard one
  523.     character at a time and edits on the fly, rejecting invalid
  524.     characters.  COL and ROW tell where to begin the data input
  525.     field, and MAXLEN is the maximum length of the string to be
  526.     returned.
  527.  
  528.     Revised 6/04/85 -- WPM }
  529.  
  530.     var
  531.         ch   : char ;     { character from keyboard }
  532.         key  : integer ;  { ord(ch) }
  533.         p    : byte ;     { position of char to left of cursor }
  534.  
  535.     procedure add_to_str ;
  536.         begin
  537.           if not (length(st) = maxlen) then
  538.             begin
  539.               p := p + 1 ;
  540.               insert (ch,st,p) ;
  541.               write (copy(st,p,maxlen))
  542.             end
  543.           else
  544.               error_buzz
  545.         end ; {--- of add_to_str ---}
  546.  
  547.     begin {--- read_str ---}
  548.         write_str (st, col, row) ;
  549.         write (build_str(chr(filler),maxlen - length(st))) ;
  550.         p := length(st) ;
  551.         repeat
  552.             gotoxy (col + p, row) ;
  553.             keyin (ch) ;
  554.             key := ord(ch) ;
  555.             if key in [$20 .. $7E] then  { printable character }
  556.                 add_to_str
  557.             else if key in adjusting then
  558.                 adjust_str (st,p,key,maxlen,col,row)
  559.             else if key in terminating then
  560.                 do_fld_ctl (key)
  561.             else
  562.                 error_buzz
  563.         until key in terminating ;
  564.         gotoxy (col + length(st), row) ;
  565.         write ('':maxlen - length(st))
  566.     end ; {--- of read_str ---}
  567.  
  568. { -------------------------------------------------------------------------- }
  569.  
  570. procedure read_int (var int:integer ; maxlen, col, row:byte) ;
  571.  
  572.   { Read Integer.  This procedure gets input from the keyboard
  573.     one character at a time and edits on the fly, rejecting
  574.     invalid characters.  COL and ROW tell where to begin the data
  575.     input field, and MAXLEN is the maximum length of the integer
  576.     to be returned.
  577.  
  578.     Revised 6/04/85 -- WPM }
  579.  
  580.     const
  581.         maxst : string[5] = '32767' ;  { string representation of maxint }
  582.  
  583.     var
  584.         ch    : char ;       { character from keyboard }
  585.         key   : integer ;    { ord(ch) }
  586.         p     : byte ;       { position of char to left of cursor }
  587.         st    : string[5] ;  { string representation of integer }
  588.         code  : integer ;    { result of string to integer conversion }
  589.  
  590.     procedure add_to_str ;
  591.         begin
  592.           if not (length(st) = maxlen) then
  593.             begin
  594.               p := p + 1 ;
  595.               insert (ch,st,p) ;
  596.               write (copy(st,p,maxlen))
  597.             end
  598.           else
  599.               error_buzz
  600.         end ; {--- of add_to_str---}
  601.  
  602.     begin {--- read_int ---}
  603.         str (int:maxlen, st) ;          { convert integer into string }
  604.         st := purgech (st, ' ') ;
  605.         st := stripch (st, '0') ;
  606.         write_str (st, col, row) ;
  607.         write (build_str(chr(filler),maxlen - length(st))) ;
  608.         p := length(st) ;
  609.         repeat
  610.             gotoxy (col + p, row) ;
  611.             keyin (ch) ;
  612.             key := ord(ch) ;
  613.             if key = $2D then                 { minus sign }
  614.               begin
  615.                 if  (pos('-',st) = 0)
  616.                 and (length(st) < maxlen)
  617.                 and (p = 0) then
  618.                     add_to_str
  619.                 else
  620.                     error_buzz
  621.               end
  622.             else if key in [$30 .. $39] then  {digits 0 - 9}
  623.               begin
  624.                 add_to_str ;
  625.                 if (length(st) = 5)
  626.                 and (st > maxst) then
  627.                   begin
  628.                     delete (st,p,1) ;
  629.                     write (^H,copy(st,p,maxlen),chr(filler)) ;
  630.                     p := p - 1 ;
  631.                     error_buzz
  632.                   end
  633.               end
  634.             else if key in adjusting then
  635.                 adjust_str (st,p,key,maxlen,col,row)
  636.             else if key in terminating then
  637.                 do_fld_ctl (key)
  638.             else
  639.                 error_buzz
  640.         until key in terminating ;
  641.         if (st = '')
  642.         or (st = '-') then
  643.           begin
  644.             int := 0 ;
  645.             code := 0
  646.           end
  647.         else
  648.             val (st, int, code) ;              {Make string into integer}
  649.  
  650.         if code = 0 then                       {Conversion worked OK}
  651.           begin
  652.             gotoxy (col, row) ;
  653.             write (int:maxlen)
  654.           end
  655.         else
  656.           begin
  657.             gotoxy (col+maxlen,row) ;
  658.             write ('** CONVERSION ERROR ', code) ;
  659.             halt
  660.           end
  661. end ; {--- of read_int ---}
  662.  
  663. { -------------------------------------------------------------------------- }
  664.  
  665. procedure read_longint (var lint:longint ; maxlen, col, row:byte) ;
  666.  
  667.   { Read Long Integer.  Just like read_int.
  668.  
  669.     Revised 3/19/88 -- WPM }
  670.  
  671.     const
  672.         maxst : string[10] = '2147483647' ;  { string representation
  673.                                                of maximum longint }
  674.  
  675.     var
  676.         ch    : char ;       { character from keyboard }
  677.         key   : integer ;    { ord(ch) }
  678.         p     : byte ;       { position of char to left of cursor }
  679.         st    : string[10] ; { string representation of longint }
  680.         code  : integer ;    { result of string to integer conversion }
  681.  
  682.     procedure add_to_str ;
  683.         begin
  684.           if not (length(st) = maxlen) then
  685.             begin
  686.               p := p + 1 ;
  687.               insert (ch,st,p) ;
  688.               write (copy(st,p,maxlen))
  689.             end
  690.           else
  691.               error_buzz
  692.         end ; {--- of add_to_str---}
  693.  
  694.     begin {--- read_longint ---}
  695.         str (lint:maxlen, st) ;          { convert integer into string }
  696.         st := purgech (st, ' ') ;
  697.         st := stripch (st, '0') ;
  698.         write_str (st, col, row) ;
  699.         write (build_str(chr(filler),maxlen - length(st))) ;
  700.         p := length(st) ;
  701.         repeat
  702.             gotoxy (col + p, row) ;
  703.             keyin (ch) ;
  704.             key := ord(ch) ;
  705.             if key = $2D then                 { minus sign }
  706.               begin
  707.                 if  (pos('-',st) = 0)
  708.                 and (length(st) < maxlen)
  709.                 and (p = 0) then
  710.                     add_to_str
  711.                 else
  712.                     error_buzz
  713.               end
  714.             else if key in [$30 .. $39] then  {digits 0 - 9}
  715.               begin
  716.                 add_to_str ;
  717.                 if (length(st) = 10)
  718.                 and (st > maxst) then
  719.                   begin
  720.                     delete (st,p,1) ;
  721.                     write (^H,copy(st,p,maxlen),chr(filler)) ;
  722.                     p := p - 1 ;
  723.                     error_buzz
  724.                   end
  725.               end
  726.             else if key in adjusting then
  727.                 adjust_str (st,p,key,maxlen,col,row)
  728.             else if key in terminating then
  729.                 do_fld_ctl (key)
  730.             else
  731.                 error_buzz
  732.         until key in terminating ;
  733.         if (st = '')
  734.         or (st = '-') then
  735.           begin
  736.             lint := 0 ;
  737.             code := 0
  738.           end
  739.         else
  740.             val (st, lint, code) ;             {Make string into integer}
  741.  
  742.         if code = 0 then                       {Conversion worked OK}
  743.           begin
  744.             gotoxy (col, row) ;
  745.             write (lint:maxlen)
  746.           end
  747.         else
  748.           begin
  749.             gotoxy (col+maxlen,row) ;
  750.             write ('** CONVERSION ERROR ', code) ;
  751.             halt
  752.           end
  753. end ; {--- of read_longint ---}
  754.  
  755. { -------------------------------------------------------------------------- }
  756.  
  757. function equal (r1,r2 : real) : boolean ;
  758.   { tests functional equality of two real numbers -- 4/30/85 }
  759.     begin
  760.         equal := abs(r1 - r2) < 1.0e-5
  761.     end ;  { function equal }
  762.  
  763. { -------------------------------------------------------------------------- }
  764.  
  765. function greater (r1,r2 : real) : boolean ;
  766.   { tests functional inequality of two real numbers -- 5/1/85 }
  767.     begin
  768.         greater := (r1 - r2) > 1.0e-5
  769.     end ;  { function greater }
  770.  
  771. { -------------------------------------------------------------------------- }
  772.  
  773. procedure read_real (var r:real ; maxlen,frac,col,row:byte) ;
  774.  
  775.   { Read Real.  This procedure gets input from the keyboard one character at
  776.     a time and edits on the fly, rejecting invalid characters.  COL and ROW
  777.     tell where to begin the data input field; MAXLEN is the maximum length of
  778.     the string representation of the real number, including sign and decimal
  779.     point; FRAC is the fractional part, the number of digits to right of the
  780.     decimal point.
  781.  
  782.     Note -- In Turbo the maximum number of significant digits in decimal (not
  783.     scientific) representation is 11.  In TurboBCD, the maximum number of
  784.     significant digits is 18.  It is the programmer's responsibility to limit
  785.     input and computed output to the maximum significant digits.
  786.  
  787.     Define MAXLEN as at least two more than FRAC.  When a real less than one
  788.     is written, Turbo puts a leading zero on it.  If it is negative, Turbo
  789.     puts a leading minus sign and zero.  This can corrupt your display unless
  790.     you allow space for the extra characters.
  791.  
  792.     8/5/88 -- Fixed bug when user deletes the decimal point and the number is
  793.     too big and it disappears.  Now the next digit is read correctly. }
  794.  
  795.     var
  796.         ch   : char ;       { Input character }
  797.         key  : integer ;    { ord(ch) }
  798.         p    : byte ;       { position of char to left of cursor }
  799.         st   : string[21] ; { String representation of real number -- }
  800.                             { max digits + minus sign + dec point + one extra }
  801.         code : integer ;    { Result of VAL conversion }
  802.         rlen,               { Current length of st to right of dec. pt. }
  803.         llen,               { Current length to left, including dec. pt. }
  804.         maxl,               { Max allowable to left, including dec. pt. }
  805.         posdec : byte ;     { position of decimal point in string }
  806.  
  807.   { +++++++++++++++++++++++++++++++++++++ }
  808.  
  809.     procedure compute_length ;
  810.       { Compute length of left and right portions of string }
  811.         begin
  812.             posdec := pos('.',st) ;
  813.             if posdec = 0 then                { If no dec. pt. ... }
  814.                 begin
  815.                     llen := length(st) ;      { the whole string is Left }
  816.                     rlen := 0                 { and none is Right }
  817.                 end
  818.             else    {There is a decimal point ...}
  819.                 begin
  820.                     llen := posdec ;          { Left is all up to and incl. dec. pt. }
  821.                     rlen := length(st) - llen { Right is the rest }
  822.                 end
  823.         end ; { proc compute_length }
  824.  
  825.   { +++++++++++++++++++++++++++++++++++++ }
  826.  
  827.     procedure add_to_str ;
  828.  
  829.         procedure add_it ;
  830.             begin
  831.               p := p + 1 ;
  832.               insert (ch,st,p) ;
  833.               write (copy(st,p,maxlen))
  834.             end ;
  835.  
  836.         begin {add_to_str}
  837.             posdec := pos ('.',st) ;
  838.             if ch = '.' then        { Decimal point; if room, add it }
  839.               begin
  840.                 if  (posdec = 0)
  841.                 and (length(st) - p <= frac) then
  842.                     add_it
  843.               end
  844.                                     { else it's not a decimal point }
  845.                                     { see if digit fits in whole part }
  846.             else if  (    (posdec = 0)
  847.                       and (llen < maxl - 1)  { only dec. pt. allowed in pos. maxl }
  848.                      )
  849.                  or  (    (posdec > 0)
  850.                       and (llen < maxl)
  851.                       and (p < posdec)
  852.                      ) then
  853.  
  854.                 add_it
  855.  
  856.                                     { digit is candidate for fractional part }
  857.             else if  (not(posdec = 0))
  858.                  and (p >= posdec)
  859.                  and (rlen < frac) then
  860.  
  861.                 add_it
  862.             else
  863.                 error_buzz
  864.  
  865.         end ; {--- of add_to_str---}
  866.  
  867.   { +++++++++++++++++++++++++++++++++++++ }
  868.  
  869.     begin {--- read_real ---}
  870.                               {Initialize}
  871.         maxl  := maxlen - frac ;
  872.  
  873.                               {Set up string representation of real and }
  874.                               {determine length of left & right portions}
  875.  
  876.         str(r:maxlen:frac,st) ;           {Make real into string}
  877.         st := purgech (st, ' ') ;         {Purge all blanks}
  878.         st := stripch (st, '0') ;         {Strip leading zeroes}
  879.         if not (pos('.', st) = 0) then    {If there is a dec. pt ... }
  880.             begin
  881.                 st := chopch (st, '0') ;  {Chop trailing zeroes}
  882.                 st := chopch (st, '.')    {and trailing dec. pt.}
  883.             end ;
  884.         compute_length ;
  885.  
  886.                               {Write string on console}
  887.  
  888.         write_str (st, col, row) ;
  889.         write (build_str(chr(filler),maxlen - length(st))) ;
  890.         p := length(st) ;
  891.  
  892.                               {Get input a character at a time & edit it}
  893.  
  894.         repeat
  895.             gotoxy (col + p, row) ;
  896.             compute_length ;
  897.             if (    (posdec = 0)
  898.                 and (llen > maxl - 1)
  899.                )
  900.             or (    (not (posdec = 0))
  901.                 and (llen > maxl)
  902.                )
  903.             or (rlen > frac) then                  { if number is larger than }
  904.               begin                                { spec then delete it all }
  905.                 key := del_fld ;
  906.                 adjust_str (st,p,key,maxlen,col,row) ;
  907.                 compute_length ;
  908.                 gotoxy (col,row) ;
  909.                 beep
  910.               end ;
  911.             keyin (ch) ;
  912.             key := ord(ch) ;
  913.             if key = $2D  then                      { minus sign }
  914.               begin
  915.                 if  (pos('-',st) = 0)
  916.                 and (p = 0)
  917.                 and (  (    (posdec = 0)
  918.                         and (llen < maxl - 1)
  919.                        )
  920.                     or (    (not (posdec = 0))
  921.                         and (llen < maxl)
  922.                        )
  923.                     ) then
  924.  
  925.                     add_to_str
  926.                 else
  927.                     error_buzz
  928.               end
  929.             else if key in [$2E, $30 .. $39] then   { decimal point, numeric digits }
  930.                 add_to_str
  931.             else if key in adjusting then
  932.                 adjust_str (st,p,key,maxlen,col,row)
  933.             else if key in terminating then
  934.                 do_fld_ctl (key)
  935.             else
  936.                 error_buzz
  937.         until key in terminating ;
  938.  
  939.                               {Done getting input, now convert back to real}
  940.         code := -1 ;                             {Use Code as a flag}
  941.         if (st = '')                             {If null string ... }
  942.         or (st = '.')
  943.         or (st = '-')
  944.         or (st = '-.') then
  945.           begin
  946.             r := 0.0 ;                           {Make real zero}
  947.             code := 0
  948.           end
  949.         else if (pos ('.',st) = 1) then          {If not null string, we must }
  950.             insert ('0',st,1)                    {check for a decimal point   }
  951.         else if (pos ('.',st) = 2)               {before any digits, which is }
  952.         and     (pos ('-',st) = 1) then          {OK in Turbo 3.0 but not 4.0.}
  953.             insert ('0',st,2)                    {If we find one, we insert a }
  954.                                                  {0 so conversion will work.  }
  955.  
  956.         else if (pos('.', st) = length(st)) then {If there is a trailing dec. }
  957.             delete (st,length(st),1) ;           {point we must get rid of it.}
  958.                                                  {Yet another incompatibility }
  959.                                                  {with Turbo 3.0!             }
  960.  
  961.         if code = -1 then                        {Real is not zero, so }
  962.             val (st,r,code) ;                    {convert string into real}
  963.  
  964.         if code = 0 then                         {Conversion worked OK}
  965.           begin
  966.             gotoxy (col, row) ;
  967.             write (r:maxlen:frac)                {Write the real on screen}
  968.           end
  969.         else
  970.           begin
  971.             gotoxy (col+maxlen,row) ;
  972.             write ('** CONVERSION ERROR ', code) ;
  973.             halt
  974.           end
  975. end ; {--- of read_real ---}
  976.  
  977. { -------------------------------------------------------------------------- }
  978.  
  979. procedure read_yn (var bool:boolean; col,row:byte) ;
  980.   { Inputs "Y" OR "N" to boolean at column and row specified,
  981.     prints "YES" or "NO."
  982.  
  983.     Note -- use this when the screen control will not return
  984.     to the question and the boolean IS NOT defined before the
  985.     user answers the question.  Does not affect global FLD. }
  986.  
  987.     var ch:char ;
  988.     begin
  989.         gotoxy (col,row) ;
  990.         write ('   ') ;
  991.         gotoxy (col,row) ;
  992.         repeat
  993.             keyin (ch) ;
  994.             ch := upcase(ch) ;
  995.             if not (ch in ['Y','N']) then error_buzz
  996.         until (ch in ['Y','N']) ;
  997.         if (ch = 'Y') then
  998.             begin
  999.                 write ('YES') ;
  1000.                 bool := true
  1001.             end
  1002.         else
  1003.             begin
  1004.                 write ('NO ') ;
  1005.                 bool := false
  1006.             end
  1007.     end ; { proc read_yn }
  1008.  
  1009. { ------------------------------------------------------------ }
  1010.  
  1011. procedure read_bool (var bool:boolean; col,row:byte) ;
  1012.   { Displays boolean at column and row specified, inputs "Y"
  1013.     or "N" to set new value of boolean, prints "YES" or "NO."
  1014.     Boolean is "forced;" user cannot cursor forward past undefined
  1015.     boolean.  Pressing "Y" or "N" terminates entry.
  1016.  
  1017.     Boolean is stored as one byte:
  1018.         $80 = undefined
  1019.         $01 = true
  1020.         $00 = false.
  1021.     Note : Turbo interprets $80 as true because it is greater than zero! }
  1022.  
  1023.     var
  1024.         ch  : char ;
  1025.         key : integer ;
  1026.  
  1027.     begin
  1028.         write_bool (bool, col, row) ;
  1029.         gotoxy (col, row) ;
  1030.         repeat
  1031.             keyin (ch) ;
  1032.             key := ord(ch) ;
  1033.             if key in [$59,$79] then          { 'Y','y' }
  1034.               begin
  1035.                 bool := true ;
  1036.                 key  := next_fld ;
  1037.                 do_fld_ctl(key)
  1038.               end
  1039.             else if key in [$4E, $6E] then    { 'N','n' }
  1040.               begin
  1041.                 bool := false ;
  1042.                 key  := next_fld ;
  1043.                 do_fld_ctl(key)
  1044.               end
  1045.             else if key in terminating then
  1046.               begin
  1047.                 if  (not defined(bool))
  1048.                 and (key in [carr_rtn, next_fld, next_page]) then
  1049.                   begin
  1050.                     key := $00 ;
  1051.                     error_buzz
  1052.                   end
  1053.                 else
  1054.                     do_fld_ctl (key)
  1055.               end
  1056.             else
  1057.                 error_buzz
  1058.         until key in terminating ;
  1059.         write_bool (bool, col, row)
  1060.     end ; {--- of read_bool ---}
  1061.  
  1062. { -------------------------------------------------------------------------- }
  1063.  
  1064. procedure pause ;
  1065.     {Prints message on bottom line, waits for user response}
  1066.     var
  1067.         ch        : char ;
  1068.         key       : integer ;
  1069.         save_term : byteset ;
  1070.     begin
  1071.         clrline (1,24) ;
  1072.         write_str ('PRESS SPACE BAR TO CONTINUE OR UP-ARROW TO GO BACK',14,24) ;
  1073.         save_term := terminating ;
  1074.         terminating := terminating - [carr_rtn, next_page] ;
  1075.         repeat
  1076.             keyin (ch) ;
  1077.             key := ord(ch) ;
  1078.             if key = next_fld then
  1079.                 key := $00
  1080.             else if key = $20 then
  1081.                 key := next_fld ;
  1082.             if key in terminating then
  1083.                 do_fld_ctl (key)
  1084.             else
  1085.                 error_buzz
  1086.         until key in terminating ;
  1087.         terminating := save_term ;
  1088.         clrline (1,24)
  1089.     end ; { proc pause }
  1090.  
  1091. { ------------------------------------------------------------ }
  1092.  
  1093. procedure hard_pause ;
  1094.   { Like Pause, but only accepts space bar or Escape and only goes forward }
  1095.     var
  1096.         ch   : char ;
  1097.         key : integer ;
  1098.     begin
  1099.         clrline (1,24) ;
  1100.         write_str ('PRESS SPACE BAR TO CONTINUE',26,24) ;
  1101.         prior_fld := fld ;
  1102.         repeat
  1103.             keyin (ch) ;
  1104.             key := ord(ch) ;
  1105.             case key of
  1106.               $20      : fld := succ(fld) ;
  1107.               escape   : fld := maxint ;
  1108.               else
  1109.                   error_buzz
  1110.             end ;
  1111.         until key in [$20, escape] ;
  1112.         clrline (1,24)
  1113.     end ; { proc hard_pause }
  1114.  
  1115. { ------------------------------------------------------------ }
  1116.  
  1117. procedure rvson ;
  1118. { turn reverse video on }
  1119.   begin
  1120.     textcolor(bgcolor) ;
  1121.     textbackground(txcolor)
  1122.   end ;
  1123.  
  1124. procedure rvsoff ;
  1125. { turn reverse video off }
  1126.   begin
  1127.     textcolor(txcolor) ;
  1128.     textbackground(bgcolor)
  1129.   end ;
  1130.  
  1131. procedure emphon ;
  1132. { turn emphasis on -- if text is dim, make it bright; if bright make it dim }
  1133.   begin
  1134.     if txcolor in [0..7] then
  1135.       textcolor(txcolor + 8)
  1136.     else
  1137.       textcolor(txcolor - 8)
  1138.   end ;
  1139.  
  1140. procedure emphoff ;
  1141. { turn emphasis off }
  1142.   begin
  1143.       textcolor(txcolor)
  1144.   end ;
  1145.  
  1146. { ------------------------------------------------------------- }
  1147.  
  1148. procedure assigncolors ;
  1149.   { change colors on display -- border same as background, but only on CGA }
  1150.   var
  1151.     regs : registers ;
  1152.  
  1153.   begin
  1154.     textbackground(bgcolor) ;       { set background color }
  1155.     regs.AX := $0B00  ;             { set border on CGA }
  1156.     regs.BX := bgcolor and $00FF ;
  1157.     intr($10,regs) ;
  1158.     textcolor(txcolor)              { set text color }
  1159.   end ;  { proc assigncolors }
  1160.  
  1161. { ------------------------------------------------------------- }
  1162.  
  1163. procedure getdrive (var drive : str1) ;
  1164.   { get current drive }
  1165.  
  1166.     var regs : registers ;
  1167.  
  1168.     begin
  1169.       with regs do
  1170.         begin
  1171.           AX := $1900 ;
  1172.           msdos(Dos.Registers(regs)) ;
  1173.           drive := chr(AL + $41)
  1174.         end
  1175.     end ; { proc getdrive }
  1176.  
  1177. { ------------------------------------------------------------- }
  1178.  
  1179. procedure show_msg (msg : str_type) ;
  1180.   { Beeps, displays message centered on line 23, pauses }
  1181.  
  1182.     var
  1183.         savefld : integer ;
  1184.  
  1185.     begin
  1186.         savefld := fld ;
  1187.         beep ;
  1188.         clrline (1,23) ;
  1189.         write_str (msg,((80-length(msg)) div 2),23) ;
  1190.         hard_pause ;
  1191.         clrline (1,23) ;
  1192.         fld := savefld ;
  1193.     end ; { proc show_msg }
  1194.  
  1195. { ---- Initialization code ---------------------------------------------- }
  1196.  
  1197. begin                              { set up global environment }
  1198.     regs.AX := $0F00 ;
  1199.     intr ($10,regs) ;
  1200.     is_mono := (regs.AL = 7) ;     { get video mode }
  1201.     if is_mono then
  1202.       begin
  1203.         bgcolor  := 0 ;
  1204.         txcolor  := 7 ;
  1205.         vid_base := $B000
  1206.       end
  1207.     else
  1208.       begin
  1209.         bgcolor  := 1 ;
  1210.         txcolor  := 7 ;
  1211.         vid_base := $B800
  1212.       end ;
  1213.     assigncolors ;                 { turn on screen colors }
  1214.     checkbreak := false            { do not allow Ctrl-Break to halt pgm }
  1215. end. { implementation }
  1216.  
  1217. { ----- EOF IO24.PAS ---------------------------------------------------- }
  1218.